// slbModel.go
package models

import (
	"errors"
	"fmt"

	//"fmt"
	"log"
	"net"
	"strings"
	"time"

	//"strconv"

	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/core/validation"
)

type Xf_waf_blackip struct {
	Id         int64  `json:"id"`
	Appname    string `json:"appname" valid:"Required"`
	Ip         string `json:"ip" valid:"Required"`
	Remarks    string `json:"remarks"`
	Status     string `json:"status"`
	Expiretime string `json:"expiretime"`
	Updatetime string `json:"updatetime"`
	Createtime int64  `json:"createtime"`
}

type Blackip struct {
	Ip         string `json:"ip" valid:"Required"`
	Status     int64  `json:"status"`
	Expiretime int64  `json:"expiretime"`
	Createtime int64  `json:"createtime"`
}

func init() {
	orm.Debug = true
	orm.RegisterModel(new(Xf_waf_blackip))
}

func checkBlackIp(u *Xf_waf_blackip) (err error) {
	valid := validation.Validation{}
	b, _ := valid.Valid(&u)
	if !b {
		for _, err := range valid.Errors {
			log.Println(err.Key, err.Message)
			return errors.New(err.Message)
		}
	}
	return nil
}

//List
//func GetXf_waf_blackipList(appname, ruletype string, id int64) (xf_waf_rule []orm.Params, count int64) {
func GetXf_waf_blackipList(appname, ip string, id, page, page_size int64) (xfr []Xf_waf_blackip, count int64) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(Xf_waf_blackip))
	cond := orm.NewCondition()
	var offset int64
	if page <= 1 {
		offset = 0
	} else {
		offset = (page - 1) * page_size
	}
	if id > 0 {
		cond = cond.And("id", id)
	}
	if ip != "" {
		cond = cond.And("ip", ip)
	}
	cond = cond.And("status__gte", 0)
	if appname != "" {
		var appNames string
		appAry := strings.Split(appname, "-")
		cond2 := orm.NewCondition()
		if len(appAry) > 1 {
			appNames = appAry[0]
			cond = cond.AndCond(cond2.Or("appname", appname).Or("appname", appNames).Or("appname", "sys"))
		} else {
			cond = cond.AndCond(cond2.Or("appname", appname).Or("appname", "sys"))
		}
	}
	qs = qs.SetCond(cond)
	qs.Limit(page_size, offset).OrderBy("-id").All(&xfr)
	//qs.All(&xfr)
	count, _ = qs.Count()
	return xfr, count
}

func GetBlackIp(appname, cfgv string) (stu []*Blackip, count int64) {
	o := orm.NewOrm()
	//var stu []Blackip
	var app string
	appAry := strings.Split(appname, "-")
	if len(appAry) > 1 {
		app = appAry[0]
	}
	var sqlstr string
	if cfgv == "0" || cfgv == "" {
		sqlstr = fmt.Sprintf("SELECT ip,status,expiretime,createtime FROM xf_waf_blackip WHERE appname = 'sys' OR appname = '%s' OR appname='%s'", app, appname)
	} else {
		sTime := time.Now().Add(-1 * time.Hour).Format("2006-01-02 15:04:05")
		sqlstr = fmt.Sprintf("SELECT ip,status,expiretime,createtime FROM xf_waf_blackip WHERE updatetime > '%s' AND (appname = 'sys' OR appname = '%s' OR appname='%s')", sTime, app, appname)
	}
	log.Println(sqlstr)
	num, err := o.Raw(sqlstr).QueryRows(&stu)
	if err == nil && num > 0 {
		return stu, num
	}
	return nil, 0
}

//添加
func AddXf_waf_blackip(u *Xf_waf_blackip) (int64, error) {
	if err := checkBlackIp(u); err != nil {
		return 0, err
	}
	o := orm.NewOrm()
	iplist := strings.Split(u.Ip, ",")
	var num int64
	for _, ip := range iplist {
		if strings.Contains(ip, "127.0.0") {
			log.Printf("[WARN] localIp no add,ip:%s\n", ip)
			continue
		}

		Xf_waf_blackip := new(Xf_waf_blackip)
		Xf_waf_blackip.Appname = u.Appname
		Xf_waf_blackip.Ip = ip
		Xf_waf_blackip.Remarks = u.Remarks
		Xf_waf_blackip.Status = "1"
		Xf_waf_blackip.Expiretime = u.Expiretime
		//Xf_waf_blackip.Createtime = time.Now().Format("2006-01-02 15:04:05")
		Xf_waf_blackip.Createtime = time.Now().Unix()
		Xf_waf_blackip.Updatetime = time.Now().Format("2006-01-02 15:04:05")
		_, err := o.Insert(Xf_waf_blackip)
		if err == nil {
			num += 1
		}
	}
	if num > 0 {
		UpdateConfigTime(u.Appname)
	}
	return num, nil
}

// //更新
func UpdateXf_waf_blackip(u *Xf_waf_blackip) (int64, error) {
	if err := checkBlackIp(u); err != nil {
		return 0, err
	}
	o := orm.NewOrm()
	op := make(orm.Params)

	if len(u.Appname) > 0 {
		op["Appname"] = u.Appname
	}
	if len(u.Ip) > 0 {
		op["Ip"] = u.Ip
	}
	if len(u.Expiretime) > 0 {
		op["Expiretime"] = u.Expiretime
	}
	if len(u.Remarks) > 0 {
		op["Remarks"] = u.Remarks
	}
	op["Updatetime"] = time.Now().Format("2006-01-02 15:04:05")
	if len(op) == 0 {
		return 0, errors.New("update field is empty")
	}
	var table Xf_waf_blackip
	num, err := o.QueryTable(table).Filter("Id", u.Id).Update(op)
	return num, err
}

func DelIpById(id int64, appname string) (int64, error) {
	//o := orm.NewOrm()
	//status, err := o.Delete(&Xf_waf_blackip{Id: id})
	//return status, err
	o := orm.NewOrm()
	sqlstr := fmt.Sprintf("UPDATE xf_waf_blackip SET status = '-1' WHERE id = %d", id)
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		log.Printf("[INFO] DelIpById affected nums:%d\n", num)
		UpdateConfigTime(appname)
		return num, nil
	}
	return 0, err
}

func EditBlackIp(id, appname, key, value string) (int64, error) {
	o := orm.NewOrm()
	nowT := time.Now().Unix()
	sqlstr := fmt.Sprintf("UPDATE xf_waf_blackip SET %s = '%s',createtime = %d WHERE id = %s", key, value, nowT, id)
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		log.Printf("[INFO] EditBlackIp affected nums:%d\n", num)
		UpdateConfigTime(appname)
		return num, nil
	}
	return 0, err
}

//将所有黑名单禁用时长过期的IP设置为禁用
func DisableExpireTimeIp() (int64, error) {
	o := orm.NewOrm()
	nowT := time.Now().Unix()
	dt := time.Now().Format("2006-01-02 15:04:05")
	//UPDATE xf_waf_blackip xwb,xf_waf_config xwc SET xwb.status = 0, xwc.updatetime = '%s' WHERE xwc.appname = xwb.appname AND xwb.expiretime > 0 AND xwb.createtime + xwb.expiretime > 16765978
	sqlstr := fmt.Sprintf("UPDATE xf_waf_blackip xwb,xf_waf_config xwc SET xwb.status = 0, xwc.updatetime = '%s' WHERE xwc.appname = xwb.appname AND xwb.expiretime > 0 AND xwb.createtime + xwb.expiretime <= %d AND xwb.status = 1", dt, nowT)
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		log.Printf("[INFO] EditBlackIp affected nums:%d\n", num)
		return num, nil
	}
	return 0, err
}

//删除IP是将IP的状态改成-1,因为要给nginx配置生效的缓冲时间，所以删除IP进行异步处理
func DelIpByStatusIsDelete() (int64, error) {
	o := orm.NewOrm()
	dt := time.Now().Add(-5 * time.Minute).Format("2006-01-02T15:04:05")
	sqlstr := fmt.Sprintf("DELETE FROM xf_waf_blackip WHERE status = -1 AND updatetime < '%s'", dt)
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		log.Printf("[INFO] DelIpByStatusIsDelete affected nums:%d\n", num)
		return num, nil
	}
	return 0, err
}

//判断IP是否为信任，避免误添加进黑名单,正常IP返回true,否则返回false
func JudgeWhiteIp(ip string) (bool, string) {
	//判断是否为信任
	if JudgeTrustIpFromFile(ip) {
		return true, fmt.Sprintf("%s:OtherTrustIP", ip)
	}
	//判断是否内网IP
	//10.0.0.0/8，172.16.0.0/12，192.168.0.0/16
	netip := net.ParseIP(ip)
	_, ipNet, _ := net.ParseCIDR("10.0.0.0/8")
	_, ipNet2, _ := net.ParseCIDR("172.16.0.0/12")
	_, ipNet3, _ := net.ParseCIDR("192.168.0.0/16")
	if ipNet.Contains(netip) || ipNet2.Contains(netip) || ipNet3.Contains(netip) {
		return true, fmt.Sprintf("%s:内网Ip", ip)
	}

	//通过情报库里的WhitelistIP，判断是否为信任IP
	_, num := JudgeOkIpFromRiskIp(ip)
	if num > 0 {
		return true, fmt.Sprintf("%s:WhitelistIP", ip)
	}
	return false, ""
}
